#pragma once

#include <signal.h>
#include <iostream>
#include <pthread.h>
#include "TcpServer.hpp"
#include "Protocol.hpp"
#include "Log.hpp"
#include "Task.hpp"
#include "ThreadPool.hpp"

#define PORT 8081

class HttpServer{
private:
    int port;
    // TcpServer *tcp_server; 我们将TCPServer改成单例模式，这样就不需要在HTTPServer中创建一个TcpServer了
    // ThreadPool thread_pool; 我们将ThreadPool改成单例模式，这样就不需要在HTTPServer中创建一个ThreadPool了
    bool stop;
public:
    HttpServer(int _port = PORT): port(_port), stop(false){}
    void InitServer()
    {
        // 调用signal函数，将当前进程收到信号SIGPIPE之后的处理方式设置为忽略该信号
        signal(SIGPIPE, SIG_IGN); 
        //为什么要干这一步？
            // 当对端关闭连接之后，那边的操作系统会通知你这边的操作系统，然后你这边的操作系统就会向当前进程发送信号SIGPIPE
            // 当前进程收到信号SIGPIPE之后就会被终止，
            // 但我们不想让httpserver进程直接终止，我们还想让它继续开着，接收新的请求
            // 所以我们就需要对这个信号进行忽略，就算对面关了，给我发了这个信号，我也不搭理你，我该写啥还写啥，能写多少是多少
            // 如果不忽略，在写入时候，server可能直接崩溃

        
    }
    void Loop()
    {
        // 获取TCPserver的单例
        TcpServer* tcp_server = TcpServer::getinstance(port);
        // 获取ThreadPool的单例
        ThreadPool* thread_pool=ThreadPool::getinstance();
        // 本来我们应该在InitServer时完成对TCPserver和ThreadPool对象的创建与初始化
        // 现在有了单例模式之后，这个工作就不用HTTPserver来干了，而是交给TCPserver和ThreadPool类中的GETinstance自己干

        LOG(INFO,"Loop begin");
        // 获取TCP连接的监听套接字
        int listen_sock = tcp_server->Sock();
        while(!stop){
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sock = accept(listen_sock, (struct sockaddr*)&peer, &len);
            // 调用accept，从请求队列中取出一个请求，将发出这个请求的客户端套接字记录在peer中
            // 传入 &len 一是告知 accept 函数客户端地址结构的初始大小，确保地址信息能够安全地存储到指定的缓冲区中；二是accept 函数返回实际存储的地址信息的长度存储在peer中，方便调用者进行后续处理。
            if(sock < 0){
                continue;
            }
            LOG(INFO,"Get a new link");
            // 走到这里说明请求从队列中成功提出，接下来我们就创建一个线程用来处理这个请求
                // int *_sock = new int(sock);
                // pthread_t tid;
                // // 注意这里pthread_create第三个参数是具体线程执行函数的函数指针，第四个参数是传入具体线程执行函数的参数（默认类型void*）
                // pthread_create(&tid, nullptr, Entrance::HandlerRequest, _sock);// 这里传参时最后为什么不能直接传&sock，而是要拷贝一份之后再传指针呢？
                // // 答：（1）生命周期问题：sock 是一个局部变量，它的生命周期仅限于定义它的代码块。当主线程中的代码块执行完毕后，sock 会被销毁，其占用的内存空间会被释放。
                // //          而新创建的线程可能在主线程的代码块执行完毕之后才开始访问 &sock 指向的内存。此时，&sock 指向的内存已经被释放，新线程访问这块内存会导致未定义行为，例如读取到无效的数据或者引发段错误。
                // //     （2）多线程并发问题：在多线程环境下，如果多个线程同时使用同一个局部变量的地址，会导致数据竞争问题。假设在一个循环中不断接受新的连接并创建新线程：当新线程还未开始访问 &sock 时，主线程可能已经接受了下一个连接，sock 的值会被更新。这样，新线程最终访问到的 sock 值可能不是它应该处理的连接的套接字描述符。
                // // 将这个子线程与创建这个子线程的主线程分离
                // pthread_detach(tid);
            // 引入线程池之后，我们不再创建一个新线程用来处理请求
            // 而是根据通信套接字sock，创建一个连接任务，然后将这个任务放入线程池中
            Task task(sock);
            // 将创建好的任务放入线程池
            thread_pool->PushTask(task);
            // 也可以直接用ThreadPool::getinstance()->PushTask(task);
        }
    }
    ~HttpServer()
    {}
};